package com.gopi.remfilebrowser.gui;

import java.awt.Component;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.io.File;
import java.util.Set;
import java.util.TreeSet;

import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JSplitPane;
import javax.swing.JTextField;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.border.BevelBorder;
import javax.swing.event.TreeExpansionEvent;
import javax.swing.event.TreeExpansionListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;

import com.gopi.remfilebrowser.util.RemoteFileBrowser;
import com.gopi.utilities.action.AbstractController;
import com.gopi.utilities.action.MenuItemDescriptor;
import com.gopi.utilities.common.Disposable;
import com.gopi.utilities.gui.GuiFactory;
import com.gopi.utilities.gui.ImageLoader;
import com.gopi.utilities.gui.ScrollTree;
import com.gopi.utilities.gui.SwingDataHandler;

public class BrowserClient implements Disposable
{
	public static final int NOT_CONNECTED = 0;
	public static final int CONNECTED = 0;
	
	// becare ful before you edit this.. used in many place with index
	public static final String[] ICON_KEYS = {"na_lonk.gif","delete","copy","paste"};
	
	private JFrame frame;
	private JLabel statusLabel;
	
	private ScrollTree localTree;
	private ScrollTree remoteTree;
	
	private FileBrowserTreeNode localTreeRoot;
	private FileBrowserTreeNode remoteTreeRoot;
	
	private int clientState;
	private String serverIp;
	
	private JTextField serverIpField;
	private JButton connectButton;
	private JButton disConnectButton;
	private ActionListener listener;
	private TreeExpansionListener expandListener;
	private TreeSelectionListener selectionListener;
	private JTree focusedTree;
	private FocusListener focusListener;
	private AbstractController controller;
	
	public BrowserClient()
	{
		listener = new BrowserClientActionListener();
		controller = BrowserClientController.getInstance();
	}
	
	public void init()
	{
		clientState = NOT_CONNECTED;
		loadIcons();
		GUIUtil.setUpDefaultLF();
		frame = new JFrame("Browser Client");
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		buildControls();
	}

	private void loadIcons()
	{
		ImageLoader.getInstance().loadIcon(FileBrowserTreeNode.ICON_KEY);
		ImageLoader.getInstance().loadIcon(ICON_KEYS);
	}
	
	private void buildControls()
	{
		JPanel panel = new JPanel(new GridBagLayout());
		frame.setContentPane(panel);
		GridBagConstraints gc = new GridBagConstraints();
		gc.anchor = GridBagConstraints.NORTH;
		gc.weightx = 100;
		gc.weighty = 1;
		//gc.weighty = 0;
		gc.gridy = 0;
		gc.gridx = 0;
		gc.fill = GridBagConstraints.HORIZONTAL;
		panel.add(getAddressBar(),gc);
		gc.gridy++;
		gc.fill = GridBagConstraints.NONE;
		gc.anchor = GridBagConstraints.WEST;
		gc.weightx = gc.weighty = 0;
		panel.add(getToolBarPanel(),gc);
		gc.gridy++;
		gc.weightx = gc.weighty = 100;
		gc.fill = GridBagConstraints.BOTH;
		panel.add(getMainPanel(),gc);
		gc.weighty = 0;
		gc.fill = GridBagConstraints.HORIZONTAL;
		gc.anchor = GridBagConstraints.SOUTH;
		gc.gridy++;
		panel.add(getStatusBar(),gc);
		buildPopupMenus();
	}
	

	private JPanel getStatusBar()
	{
		JPanel panel = GUIUtil.createGridBagPanel();
		panel.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED));
		GridBagConstraints gc = GUIUtil.createDefaultGridBagConstraint();
		gc.anchor = GridBagConstraints.WEST;
		gc.weightx = 100;
		GUIUtil.fillRowWithComponent(panel,statusLabel = new JLabel("Not Connected..."),gc);
		return panel;
	}
	
	private JComponent getMainPanel()
	{
		JSplitPane sp = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
		sp.setTopComponent(getLeftPanel());
		sp.setBottomComponent(getRightPanel());
		return sp;
	}
	
	private JPanel getLeftPanel()
	{
		JPanel pan = GUIUtil.createGridBagPanel();
		remoteTreeRoot = new FileBrowserTreeNode();
		remoteTreeRoot.setNodeType(FileBrowserTreeNode.HOST_ROOT_UNCONNECTED);
		remoteTree = new ScrollTree(remoteTreeRoot,true);
		remoteTree.getTree().addTreeExpansionListener(expandListener = new RemoteTreeExpansionListener());
		remoteTree.getTree().addTreeSelectionListener(selectionListener = new RemoteTreeSelectionListener());
		remoteTree.getTree().addFocusListener(focusListener = new BrowserTreeFocusListener());
		remoteTree.getTree().setName("Tree1");
		GUIUtil.fillComponent(pan,remoteTree.getRenderComponent());
		return pan;
	}
	
	private JPanel getRightPanel()
	{
		JPanel pan = GUIUtil.createGridBagPanel();
		localTreeRoot = new FileBrowserTreeNode(FileBrowserTreeNode.LOCAL_HOST);
		localTree = new ScrollTree(localTreeRoot,true);
		localTree.getTree().addTreeExpansionListener(expandListener);
		localTree.getTree().addTreeSelectionListener(selectionListener);
		localTree.getTree().addFocusListener(focusListener);
		localTree.getTree().setName("Tree2");
		expandLocalTree(null);
		localTree.getTree().expandRow(0);
		localTreeRoot.setDirty(false);
		GUIUtil.fillComponent(pan,localTree.getRenderComponent());
		return pan;
	}
	
	private void buildPopupMenus()
	{
		localTree.initPopup();
		MenuItemDescriptor[] dir_node_menu = new MenuItemDescriptor[] {
			new MenuItemDescriptor("   Copy",BrowserClientController.COPY_ACTION,"copy",listener),
			new MenuItemDescriptor("   Paste",BrowserClientController.PASTE_ACTION,"paste",listener),
			new MenuItemDescriptor("   Properties",BrowserClientController.PROPERTY_ACTION,null,listener),
		};
		localTree.addPopupMenu(FileBrowserTreeNode.DIR, MenuItemDescriptor.getMenuList(dir_node_menu), controller);
		localTree.addPopupMenu(FileBrowserTreeNode.FILE, MenuItemDescriptor.getMenuList(dir_node_menu), controller);
		controller.disableCommand(BrowserClientController.PASTE_ACTION);
	}
	
	public void setStatus(final String status)
	{
		setStatus(status,"");
	}
	
	public void setStatus(final String status,final String iconKey)
	{
		System.out.println(ImageLoader.getInstance().getIcon(iconKey));
		if(SwingUtilities.isEventDispatchThread())
		{
			statusLabel.setText(status);
			statusLabel.setIcon(ImageLoader.getInstance().getIcon(iconKey));
		}
		else
			SwingUtilities.invokeLater(new Runnable() { 
				public void run() 
				{ 
					statusLabel.setText(status); 
					statusLabel.setIcon(ImageLoader.getInstance().getIcon(iconKey));
				} 
			});
	}
	
	private JPanel getAddressBar()
	{
		JPanel panel = new JPanel(new GridBagLayout());
		GridBagConstraints gc = GUIUtil.createDefaultGridBagConstraint();
		gc.anchor = GridBagConstraints.NORTH;
		gc.fill = GridBagConstraints.HORIZONTAL;
		gc.insets.top = gc.insets.bottom = 20;
		GUIUtil.fillRowWithComponents(panel,
				new Component[] {
				new JLabel("Connect to : "),
				serverIpField = GuiFactory.createTextField("", 20),
				connectButton = GuiFactory.createButton("Connect", BrowserClientController.CON_ACTION, (Icon) null, listener,controller),
				disConnectButton = GuiFactory.createButton("Disconnect", BrowserClientController.DIS_CON_ACTION
						, (Icon) null, listener,controller),
			},
			new int[] {0,10,0,0},
			gc);
		disConnectButton.setVisible(false);
		return panel;
	}
	
	private JPanel getToolBarPanel()
	{
		JPanel panel = GUIUtil.createGridBagPanel();
		GridBagConstraints gc = new GridBagConstraints();
		gc.weightx = gc.weighty = 0;
		gc.gridx = gc.gridy;
		GUIUtil.fillRowWithComponents(panel, 
				new Component[] {
					GuiFactory.createButton("Copy from Remote", BrowserClientController.COPY_ACTION1, ICON_KEYS[2], listener,controller),
					GuiFactory.createButton("Paste to local", BrowserClientController.PASTE_ACTION1, ICON_KEYS[3], listener,controller),
					GuiFactory.createButton("Copy from local", BrowserClientController.COPY_ACTION2, ICON_KEYS[2], listener,controller),
					GuiFactory.createButton("Paste to Remote", BrowserClientController.PASTE_ACTION2, ICON_KEYS[3], listener,controller),
					
				}, gc);
		return panel;
	}
	
	public void showSafe()
	{
		SwingUtilities.invokeLater(new Runnable() {
			public void run()
			{
				frame.pack();
				frame.setSize(700,500);
				BrowserClient.this.frame.setVisible(true);
			}
		});
	}
	
	private void expandLocalTree(TreeExpansionEvent event)
	{
		Object os[] = event==null ? new Object[] {localTreeRoot} : event.getPath().getPath();
		// level 0 is root
		StringBuffer sb = new StringBuffer();
		for(int i = 1;i<os.length;++i)
		{
			sb.append(((FileBrowserTreeNode) os[i]).getFileName() + File.separator);
		}
		if(sb.toString().length()>0)
		{
			FileBrowserTreeNode nodeParent = (FileBrowserTreeNode) os[os.length-1];
			Set<FileBrowserTreeNode> list = new TreeSet<FileBrowserTreeNode>();
			File f = new File(sb.toString());
			File files[] = f.listFiles();
			if(files!=null)
			{
				FileBrowserTreeNode newNode;
				for(File file : files)
				{
					 newNode = new FileBrowserTreeNode();
					 if(file.isDirectory())
					 {
						 newNode.setNodeType(FileBrowserTreeNode.DIR);
					 }
					 else
					 {
						 newNode.setNodeType(FileBrowserTreeNode.FILE);
					 }
					 newNode.setFileName(file.getName());
					 list.add(newNode);
					 
				}
				 for(FileBrowserTreeNode node: list)
				 {
					 localTree.addNode(nodeParent, node);
				 }
				 nodeParent.setDirty(false);
			}
		}
		else if(localTreeRoot.isDirty())
		{
			File[] files = File.listRoots();
			FileBrowserTreeNode nodeParent = (FileBrowserTreeNode) os[os.length-1];
			FileBrowserTreeNode newNode;
			Set<FileBrowserTreeNode> list = new TreeSet<FileBrowserTreeNode>();
			for(File file : files)
			{
				newNode = new FileBrowserTreeNode();
				newNode.setNodeType(FileBrowserTreeNode.DRIVE);
				newNode.setFileName(file.getPath());
				list.add(newNode);
				System.out.println(file.getPath());
			}
			for(FileBrowserTreeNode node: list)
			{
				localTree.addNode(nodeParent, node);
			}
			localTreeRoot.setDirty(false);
		}
	}
	
	class RemoteTreeSelectionListener implements TreeSelectionListener
	{

		public void valueChanged(TreeSelectionEvent e) 
		{
			String sourceName = ((JTree)e.getSource()).getName();
			//if(sourceName==)
		}
		
	}
	
	class RemoteTreeExpansionListener implements TreeExpansionListener
	{

		public void treeCollapsed(TreeExpansionEvent event) 
		{
			
		}

		public void treeExpanded(TreeExpansionEvent event) 
		{
			System.out.println(event.getSource());
			if(((JTree) event.getSource()).getName().equals(remoteTree.getTree().getName()))
			{
				Object os[] = event.getPath().getPath();
				// level 0 is root
				StringBuffer sb = new StringBuffer();
				for(int i = 1;i<os.length;++i)
				{
					sb.append(((FileBrowserTreeNode) os[i]).getFileName() + File.separator);
				}
				if(os.length>1 && ((FileBrowserTreeNode)os[os.length-1]).isDirty() )
				{
					RemoteFileBrowserHandler browser = new RemoteFileBrowserHandler(new Object[] {
							os[os.length-1],
							remoteTree,
							sb.toString()
					});
					browser.startAsyncFetch(null, null);
					
				}
			}
			else
			{
				expandLocalTree(event);
			}
		}
	}
	
	class BrowserClientActionListener implements ActionListener
	{
		public void actionPerformed(ActionEvent ae)
		{
			if(ae.getActionCommand().equals(BrowserClientController.CON_ACTION))
			{
				SwingDataHandler dataHandler = new RemoteFileBrowserHandler(new Object[] {
					remoteTreeRoot,
					remoteTree,
					"\\~"
				});
				remoteTree.getTree().expandRow(0);
				serverIpField.setEnabled(false);
				dataHandler.startAsyncFetch(ae.getActionCommand(), controller);
			}
			else if(ae.getActionCommand().equals(BrowserClientController.DIS_CON_ACTION))
			{
				disconnect();
			}
			else if(ae.getActionCommand().equals(BrowserClientController.PROPERTY_ACTION))
			{
				if(focusedTree.getName().equals("Tree1")) // remote Tree
				{
					
				}
				else // local tree
				{
					FileBrowserTreeNode node = (FileBrowserTreeNode) ScrollTree.getSelectedTreeNode(focusedTree);
					//System.out.println(node.getFilePath());
					FilePropertyDialog dialog = new FilePropertyDialog(frame);
					dialog.showFileProperty(node.getFilePath(),false,node.getNodeType()==FileBrowserTreeNode.FILE);
				}
			}
		}
	}
	
	class BrowserTreeFocusListener implements FocusListener
	{

		public void focusGained(FocusEvent e) 
		{
			
		}

		public void focusLost(FocusEvent e) 
		{
			focusedTree = (JTree) e.getSource();
		}
		
	}
	
	private void disconnect()
	{
		controller.setVisible(BrowserClientController.CON_ACTION, true);
		controller.setVisible(BrowserClientController.DIS_CON_ACTION, false);
		// perform disconnect
		
		controller.enableCommand(BrowserClientController.CON_ACTION);
		controller.disableCommand(BrowserClientController.DIS_CON_ACTION);
		setStatus("Not Connected ...");
		serverIpField.setEnabled(true);
		remoteTree.removeChild(remoteTreeRoot);
		remoteTreeRoot.setNodeType(FileBrowserTreeNode.HOST_ROOT_UNCONNECTED);
	}
	
	class RemoteFileBrowserHandler extends SwingDataHandler
	{
		private FileBrowserTreeNode parentNode;
		private ScrollTree tree;
		private Set<FileBrowserTreeNode> nodes;
		public RemoteFileBrowserHandler(Object[] objs)
		{
			super(objs);
			parentNode = (FileBrowserTreeNode) objs[0];
			tree = (ScrollTree) objs[1];
		}
		
		@Override
		public boolean fetchData() throws Exception
		{
			boolean status = true;
			FileBrowserTreeNode tempNode;
			if(action!=null && action.equals(BrowserClientController.CON_ACTION))
			{
				setStatus("Connecting to ... " + serverIpField.getText(),ICON_KEYS[0]);
			}
			tree.addNode(parentNode, tempNode = new FileBrowserTreeNode(FileBrowserTreeNode.LOADING));
			tempNode.setAllowsChildren(false);
			nodes = RemoteFileBrowser.getFiles(serverIpField.getText(), workSet[2].toString());
			return nodes!=null;
		}

		@Override
		public void updateData(boolean status, Exception e) 
		{
			tree.removeChild(parentNode);
			if(status)
			{
				for(FileBrowserTreeNode node: nodes)
				{
					tree.addNode(parentNode, node);
				}
				parentNode.setDirty(false);
			}

			if(action!=null && super.action.equals(BrowserClientController.CON_ACTION))
			{
				controller.setEnable(BrowserClientController.CON_ACTION,!status);
				controller.setVisible(BrowserClientController.CON_ACTION, !status);
				controller.setVisible(BrowserClientController.DIS_CON_ACTION, status);
				controller.setEnable(BrowserClientController.DIS_CON_ACTION, status);
				serverIpField.setEnabled(!status);
				setStatus(status ? "Connected ..." : "Error connecting to remote server ...");
				if(status)
				{
					remoteTreeRoot.setNodeType(FileBrowserTreeNode.HOST_ROOT_CONNECTED);
				}
			}
			else if(!status)
			{
				JOptionPane.showMessageDialog(frame, e.getMessage(),"Error",JOptionPane.ERROR_MESSAGE);
			}
		}
	}
	
	public void dispose()
	{
		controller.dispose();
		remoteTree.dispose();
		localTree.dispose();
	}
	
	public static void main(String[] args)
	{
		BrowserClient client = new BrowserClient();
		client.init();
		client.showSafe();
	}
}
